/*
  FUSE: Filesystem in Userspace
  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>

  This program can be distributed under the terms of the GNU GPL.
  See the file COPYING.

  gcc -Wall `pkg-config fuse --cflags --libs` hello.c -o hello
*/


//Linkear con pthread y fuse, define symbol (-D) _FILE_OFFSET_BITS=64
//Que version uso??
//Esta es 2.8.5
#define FUSE_USE_VERSION 26

//#define TESTING //Desactivar!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#ifdef TESTING
#include "testing.h"
#endif

#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>

#include "structsFAT.h" 
//#include "functionsFAT.h"
#include "fuseLayer.h"
#include "basicFunctions.h"
#include "console.h"
#include "cache.h"
#include "socketMng.h"

#include <stdlib.h>
#include "../../General/Logs/log.h"
#include "../../General/ConfigFile/config.h"
#include "../../General/Dictionary/dictionary.h"
#include <signal.h>


#define ALL_PERMISSIONS S_IEXEC | S_IREAD | S_IWRITE | S_IROTH | S_IRWXG | S_IRWXO;

static int fuse_getattr(const char *path, struct stat *stbuf)
{
	int Result = 0;
	List_FILE_INFO FileList;
	FILE_INFO aFile;
	char * fileDirectory;
	char * fileName;

	if (strcmp(path, "/.Trash") == 0) return 0;
	if (strcmp(path, "/.Trash-1000") == 0) return 0;

	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_getattr(", path, ")");

	List_FILE_INFO_Init(&FileList);
	memset(stbuf, 0, sizeof(struct stat));

	if (strcmp(path, "/") == 0) {
		stbuf->st_mode = S_IFDIR | ALL_PERMISSIONS;//0755;
		stbuf->st_nlink = 2;
		log_debug(LogFile, "", "%s%d","fuse_getattr - seteo de modo en: ", stbuf->st_mode);
		return Result;
	}

	fileDirectory = ExtractFilePath(path);
	fileName = ExtractFilename(path);
	getEntrysFromPath(fileDirectory, &FileList);
	Result = setFileInfoByFileName(&FileList, fileName, &aFile);

	if (Result != -1) {
		if (aFile.attribute == _SubDirectory)
			stbuf->st_mode = S_IFDIR | ALL_PERMISSIONS;//0755;
		if (aFile.attribute == _Archive)
			stbuf->st_mode = S_IFREG | ALL_PERMISSIONS;//0444;
		stbuf->st_size = aFile.size;
		stbuf->st_nlink = 1;
	}
	else
		Result = -ENOENT;

	List_FILE_INFO_Destroy(&FileList);
	free(fileDirectory);
	free(fileName);
	log_debug(LogFile, "", "%s%d","fuse_getattr - seteo de modo en: ", stbuf->st_mode);
	log_destroy(LogFile);
	return Result;
}

static int fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_readdir(", path, ")");


	(void) offset;
	(void) fi;

	List_FILE_INFO Listdir;
	Nodo_FILE_INFO *aDir;

	List_FILE_INFO_Init(&Listdir);

	log_info(LogFile, "", "%s","voy a llamar a getEntrysFromPath");
	getEntrysFromPath(path, &Listdir);
	log_info(LogFile, "", "%s","fin llamada a getEntrysFromPath");

	aDir = Listdir.Head;

	while (aDir != NULL)
	{
		if (strcmp(aDir->aFile.fileName, "..") != 0 && strcmp(aDir->aFile.fileName, ".") != 0)
			filler(buf, aDir->aFile.fileName, NULL, 0);
		//printf("%s\n", aDir->aFile.fileName);
		aDir = aDir->nxt;
	}

	List_FILE_INFO_Destroy(&Listdir);
	log_destroy(LogFile);
	return 0;
}

static int fuse_open(const char *path, struct fuse_file_info *fi)
{
	List_FILE_INFO FileList;
	Nodo_FILE_INFO *aFile;
	char * fileDirectory; //Directorio donde esta el archivo
	char * fileName;
	char * strlog = NULL;
	int Result = 0;

	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_open(", path, ")");
	log_destroy(LogFile);

	List_FILE_INFO_Init(&FileList);

	fileDirectory = ExtractFilePath(path);
	fileName = ExtractFilename(path);
	if (strlen(fileName) > MAX_FILENAME_SO || strlen(fileName) == 0)
		Result = -1; //La longitud del archivo es mayor que la que dice en las especificaciones del tp, entonces me voy de la funcion

	if (Result == 0)
	{
		//Obtengo todos los archivos del directorio
		getEntrysFromPath(fileDirectory, &FileList);

		//logueo de la lista de clusters del archivo (log obligatorio), verificacion que exista el archivo y iniciar archivo en cache
		aFile = FileList.Head;
		while (aFile != NULL && strcasecmp(aFile->aFile.fileName, fileName) != 0)
			aFile = aFile->nxt;
		if (aFile != NULL) { //existe el archivo
			LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
			logChainClusters(aFile->aFile.firstCluster, &strlog);
			int32_t len = strlen(strlog);
			if(len > 1024) {
				int32_t i = 1000, x = 0;
				log_required(LogFile, "", "Lista de clusters de %s: ", path);
				while(i < len) {
					while(i < len && strlog[i] != ',')
						i--;
					strlog[i] = '\0'; //trunco la cadena
					log_required(LogFile, "", "%s", &strlog[x]);
					strlog[i] = ','; //vuelvo a la cadena original
					x = i;
					i += 1000;
					if(i > len)
						i = len;
				}
			}
			else
				log_required(LogFile, "", "Lista de clusters de %s: %s", path, strlog);

			log_destroy(LogFile);
			free(strlog);
			Cache_AddFile(path);
		}
		else
			Result = -ENOENT;
	}

    List_FILE_INFO_Destroy(&FileList);
    free(fileDirectory);
    free(fileName);
    return Result;
}

static int fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
	size_t len;
	(void) fi;
	List_FILE_INFO FileList;
	FILE_INFO aFile;
	char * fileDirectory;
	char * fileName;
	char * bufferOut;

	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "Inicio funcion fuse_read(%s, size = %d, offset = %d)", path, size, offset);
	log_destroy(LogFile);

	List_FILE_INFO_Init(&FileList);

	fileDirectory = ExtractFilePath(path);
	fileName = ExtractFilename(path);

	getEntrysFromPath(fileDirectory, &FileList);
	setFileInfoByFileName(&FileList, fileName, &aFile);
	bufferOut = (char *) calloc(size, sizeof(char));

	readDataFromChainCluster(aFile.firstCluster, bufferOut, size, path, offset);
	len = aFile.size;
	if (offset < len) {
		if (offset + size > len)
			size = len - offset;
		memcpy(buf, bufferOut, size);
	} else
		size = 0; //Si el offset que quiero leer es mayor que el tamaño del archivo entonces algo no anda bien

	List_FILE_INFO_Destroy(&FileList);
	free(bufferOut);
	free(fileName);
	free(fileDirectory);
	return size;
}

static int fuse_create(const char * path, mode_t mode, struct fuse_file_info *fi)
{
	List_FILE_INFO FileList;
	Nodo_FILE_INFO *aFile;
	FILE_INFO aNewFile;

	char * fileDirectory; //Directorio donde esta el archivo
	char * fileName;
	int Result = 0;

	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_create(", path, ")");

	fileDirectory = ExtractFilePath(path);
	fileName = ExtractFilename(path);

	if (strlen(fileName) == MAX_FILENAME_SO) {
	    free(fileDirectory);
	    free(fileName);
	    log_error(LogFile, "", "%s","El nombre del archivo posee una longitud mayor a la soportada");
	    log_destroy(LogFile);
	    return Result;
	}

	//Obtengo todos los archivos del directorio
	List_FILE_INFO_Init(&FileList);
	Result = getEntrysFromPath(fileDirectory, &FileList); //Si Result se setea en -1 el dir no existe
	aFile = FileList.Head;
	while (aFile != NULL && strcasecmp(aFile->aFile.fileName, fileName) != 0) {
		aFile = aFile->nxt;
	}

	if (aFile == NULL && Result != -1) {
		aNewFile.attribute = _Archive;
		aNewFile.firstCluster = 0;
		aNewFile.size = 0;
		assignCreateDate(&aNewFile);
		strcpy(aNewFile.fileName, fileName);
		AddEntryToDirectory(&FileList, aNewFile, 0, 0);
	}
	else
		Result = -1;

    List_FILE_INFO_Destroy(&FileList);
    free(fileDirectory);
    free(fileName);

    log_info(LogFile, "", "%s%d","Resultado de fuse_create = ", Result);
    log_destroy(LogFile);
    return Result;
}

static int fuse_flush(const char *path, struct fuse_file_info *fi)
{
	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_flush(", path, ")");
	log_destroy(LogFile);

	Cache_Flush(path);
	return 0;
}

static int fuse_mkdir(const char * path, mode_t mode)
{
	List_FILE_INFO FileList;
	FILE_INFO newDir;
	char * ParentDirectory;
	char * dirName;
	int Result = 0;

	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s","Inicio funcion fuse_mkdir");
	log_destroy(LogFile);

	List_FILE_INFO_Init(&FileList);

	if (getEntrysFromPath(path, &FileList) == 0) {//Intento obtener entradas del directorio
		//El directorio que se quiere agregar ya existe
		List_FILE_INFO_Destroy(&FileList);
		return -1;
	}

	dirName = ExtractFilename(path);
	strcpy(newDir.fileName, dirName);
	newDir.firstCluster = getFreeCluster();
	newDir.size = 0;
	newDir.attribute = 0x10; //Subdirectorio
	assignCreateDate(&newDir);
	List_FILE_INFO_Destroy(&FileList);

	ParentDirectory = ExtractFilePath(path);
	getEntrysFromPath(ParentDirectory, &FileList); //obtengo las entradas del directorio en donde hay que agregar la nueva entrada

	AddEntryToDirectory(&FileList, newDir, 1, 1);

	free(ParentDirectory);
	free(dirName);
    List_FILE_INFO_Destroy(&FileList);

    return Result;
}

static int fuse_rename(const char *from, const char *to)
{
	List_FILE_INFO FileList,
				   FileListNewDir;
	Nodo_FILE_INFO *aFile;
	FILE_INFO aFileNew;

	char * fileDirectory; //Directorio donde esta el archivo
	char * newFileDirctory;
	char * fileName;
	char * newFileName;
	int Result = 0;

	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s","Inicio funcion fuse_rename");
	log_destroy(LogFile);

	List_FILE_INFO_Init(&FileList);
	List_FILE_INFO_Init(&FileListNewDir);

	fileDirectory = ExtractFilePath(from);
	newFileDirctory = ExtractFilePath(to);
	fileName = ExtractFilename(from);
	newFileName = ExtractFilename(to);

	//Obtengo todos los archivos del directorio
	getEntrysFromPath(fileDirectory, &FileList);
	aFile = FileList.Head;
	while (aFile != NULL && strcasecmp(aFile->aFile.fileName, fileName) != 0) {
		aFile = aFile->nxt;
	}
	if (aFile != NULL) {
		aFileNew.attribute = aFile->aFile.attribute;
		aFileNew.firstCluster = aFile->aFile.firstCluster;
		aFileNew.size = aFile->aFile.size;
		aFileNew.createDate = aFile->aFile.createDate;
		strcpy(aFileNew.fileName, newFileName);
		getEntrysFromPath(newFileDirctory, &FileListNewDir);
		if (FileListNewDir.Head != NULL) {
			AddEntryToDirectory(&FileListNewDir, aFileNew, aFileNew.attribute == _SubDirectory, 0);
			RemoveEntryFromDirectory(&FileList, aFile->aFile);
		}
		else
			Result = -1;
	}
	else
		Result = -1;

    List_FILE_INFO_Destroy(&FileList);
    List_FILE_INFO_Destroy(&FileListNewDir);
    free(newFileName);
    free(newFileDirctory);
    free(fileDirectory);
    free(fileName);
    return Result;
}

static int fuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
	//Falta actualizar el cluster donde sta el archivo
    int Result;
    char * directoryName = ExtractFilePath(path);
    char * fileName = ExtractFilename(path);
    List_FILE_INFO FileList;
    FILE_INFO fileInfo;
    CLUSTER newFirstCluster;

	t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "Inicio funcion fuse_write( %s, size=%d, offset=%d)", path, size, offset);
	//Valido que el archivo exista? (se supone que antes hay que abrirlo)

	List_FILE_INFO_Init(&FileList);

	/*Primero libero la cadena de clusters*/
    getEntrysFromPath(directoryName, &FileList);

    setFileInfoByFileName(&FileList, fileName, &fileInfo);

    newFirstCluster = 0;
    if (offset > 0)
    	newFirstCluster = fileInfo.firstCluster;
    else {
    	freeChainCluster_noUp(fileInfo.firstCluster);
    	fileInfo.size = 0;
    }
    Result = writeDataToFile(path, buf, size, &newFirstCluster, offset, &fileInfo);

    fileInfo.firstCluster = newFirstCluster;
    if (offset + size > fileInfo.size)
    	fileInfo.size = offset + size;
    log_info(LogFile, "", "fuse_write - fileinfo.firstcluster = %d / fileinfo.size = %d / Result = %d",fileInfo.firstCluster, fileInfo.size, Result);
    if (Result != -1)
    	UpdateClusterFile(FileList.firstCluster, fileInfo);


	free(directoryName);
	free(fileName);
	List_FILE_INFO_Destroy(&FileList);

	log_info(LogFile, "", "%s%d","Return funcion fuse_write = ", Result);
	log_destroy(LogFile);
    return Result;
}

static int fuse_release(const char *path, int flags)
{
    /* Just a stub.  This method is optional and can safely be left
       unimplemented */
    (void) path;
    (void) flags;

    t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_release(", path, ")");
	log_destroy(LogFile);

	//Flush y desasignacion de cache
	Cache_Flush(path);
	Cache_RemoveFile(path);

    return 0;
}

static int fuse_truncate(const char *path, off_t size)
{
    int Result;
	List_FILE_INFO FileList;
	FILE_INFO aFile;
	char * fileDirectory;
	char * fileName;

    t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_truncate(", path, ")");
	log_destroy(LogFile);

	List_FILE_INFO_Init(&FileList);

	fileDirectory = ExtractFilePath(path);
	fileName = ExtractFilename(path);

	getEntrysFromPath(fileDirectory, &FileList);
	setFileInfoByFileName(&FileList, fileName, &aFile);

	if (aFile.attribute == _SubDirectory)
		Result = -1;
	else if (size == aFile.size)
		Result = 0;
	else if (size > aFile.size) {
		truncateChainCluster(&aFile, size);
		aFile.size = size;
		UpdateClusterFile(FileList.firstCluster, aFile);
	}
	else if (size < aFile.size) {
		truncateChainCluster(&aFile, size);
		aFile.size = size;
		if (size == 0)
			aFile.firstCluster = 0;
		UpdateClusterFile(FileList.firstCluster, aFile);
	}

	List_FILE_INFO_Destroy(&FileList);
	free(fileName);
	free(fileDirectory);
	return 0;
}

static int fuse_unlink(const char *path)
{
	List_FILE_INFO FileList;
	Nodo_FILE_INFO *aFile;

	char * fileDirectory; //Directorio donde esta el archivo
	char * fileName;
	int Result = 0;

    t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_unlink(", path, ")");
	log_destroy(LogFile);

	List_FILE_INFO_Init(&FileList);

	fileDirectory = ExtractFilePath(path);
	fileName = ExtractFilename(path);

	//Obtengo todos los archivos del directorio
	getEntrysFromPath(fileDirectory, &FileList);
	aFile = FileList.Head;
	while (aFile != NULL && strcasecmp(aFile->aFile.fileName, fileName) != 0) {
		aFile = aFile->nxt;
	}
	if (aFile != NULL) {
		RemoveEntryFromDirectory(&FileList, aFile->aFile);
		freeChainCluster(aFile->aFile.firstCluster);
	}
	else
		Result = -1;

    List_FILE_INFO_Destroy(&FileList);
    free(fileDirectory);
    free(fileName);

    return Result;
}

static int fuse_rmdir(const char *path)
{
	List_FILE_INFO FileList;
	Nodo_FILE_INFO *aFile;
	char * ParentDirectory;
	char * dirName;

    t_log *LogFile = log_create("ProcesoFileSystem", getLogFileName(), getLogLevel(), M_CONSOLE_DISABLE);
	log_info(LogFile, "", "%s%s%s","Inicio funcion fuse_rmdir(", path, ")");
	log_destroy(LogFile);

	int Result = 0, entrys = 0, p  = 0, p2 = 0;

	if(strcmp(path, "/") == 0 ) return -1;

	List_FILE_INFO_Init(&FileList);

	//Obtengo todos los archivos del directorio
	getEntrysFromPath(path, &FileList);

	aFile = FileList.Head;
	//Valido que el directorio este vacio (esta vacio si solo existen las entradas . y ..)
	while (aFile != NULL && entrys < 3) {
		if (strcmp(aFile->aFile.fileName, ".") == 0) p++;
		if (strcmp(aFile->aFile.fileName, "..") == 0) p2++;
		entrys++;
		aFile = aFile->nxt;
	}
	List_FILE_INFO_Destroy(&FileList);

	if(!(p==1 && p2==1 && entrys==2)) return -1;

	ParentDirectory = ExtractFilePath(path);
	dirName = ExtractFilename(path);

	getEntrysFromPath(ParentDirectory, &FileList);
	aFile = FileList.Head;
	while (aFile != NULL && strcasecmp(aFile->aFile.fileName, dirName) != 0)
		aFile = aFile->nxt;

	if (aFile != NULL) {
		RemoveEntryFromDirectory(&FileList, aFile->aFile);
		freeChainCluster(aFile->aFile.firstCluster); //Debe setear como libre el cluster donde estan . y ..
	}
	else
		Result = -1;

	free(ParentDirectory);
	free(dirName);
    List_FILE_INFO_Destroy(&FileList);

    return Result;
}

void loadConfigInformation(const char * path)
{
/*
	IPDisco xxx,xxx,xxx,xxx
	PuertoDisco xxxx
	Conexiones xx
	Cache xx
*/
	const char * configFileName = "configPFS.txt";
	char valor[30];
	char * directory;
	char * fullFileName;

	Dictionary parametros;
	Dictionary_Create(&parametros);
	Dictionary_Add(&parametros, "IPDisco", "");
	Dictionary_Add(&parametros, "PuertoDisco", "");
	Dictionary_Add(&parametros, "Conexiones", "");
	Dictionary_Add(&parametros, "Cache", "");
	Dictionary_Add(&parametros, "LogLevel", "");

	directory = ExtractFilePath(path);
	fullFileName = (char *)calloc(strlen(directory) + strlen(configFileName) + 1, sizeof(char));
	strcpy(fullFileName, directory);
	strcat(fullFileName, configFileName);
	free(directory);

	if (CargarParametros(fullFileName, &parametros) == -1) {
		perror("No se pudieron cargar los parametros de configuracion");
		printf("Archivo: %s\n", fullFileName);
		exit(EXIT_FAILURE); //No se debe seguir si no se puede levantar el configFile
	}

	Dictionary_Index(&parametros, "IPDisco", valor);
	SocketMNG_setIP(valor);
	Dictionary_Index(&parametros, "PuertoDisco", valor);
	SocketMNG_setPort((int32_t)atoi(valor));
	Dictionary_Index(&parametros, "Conexiones", valor);
	SocketMNG_setMaxConnections((int32_t)atoi(valor));
	Dictionary_Index(&parametros, "Cache", valor);
	Cache_Initialize((uint32_t)atoi(valor) * 1024);
	Dictionary_Index(&parametros, "LogLevel", valor);
	setLogLevel((int32_t)atoi(valor));

	Dictionary_Destroy(&parametros);
	free(fullFileName);
}

static struct fuse_operations operations = {
	.getattr	= fuse_getattr,
	.readdir	= fuse_readdir,
	.open		= fuse_open,
	.read		= fuse_read,
	.mkdir      = fuse_mkdir,
	.rename		= fuse_rename,
    .create  	= fuse_create,
    .write		= fuse_write,
    .flush  	= fuse_flush,
    .release	= fuse_release,
    .truncate	= fuse_truncate,
    .unlink		= fuse_unlink,
    .rmdir		= fuse_rmdir,
};

int main(int argc, char *argv[])
{
	pthread_t console_thread;
	int console_ret;
	loadConfigInformation(argv[0]);
	setWorkingDirectory(argv[0]);
	setLogFileName("logFile.txt");
	readBootSector();
	loadFileAllocationTable();
#ifdef TESTING
	goto label_testing;
#endif
//  printf("fin loadFileAllocationTable\n");
//	getchar();
	signal(SIGUSR1, Cache_Dump);
	console_ret = pthread_create(&console_thread, NULL, (void *) Console_Run, NULL);
	if (console_ret)
		perror("Fallo la creacion del thread de consola");

#ifdef TESTING
//	test_Direccionamiento();
//	CLUSTER firstCluster = GetClusterFromFAT(0x00000000);
//	CLUSTER _2ndCluster  = GetClusterFromFAT(0x00000001);
	struct fuse_file_info *fi;
	void *buf = NULL;
	fuse_fill_dir_t filler = malloc(sizeof(fuse_fill_dir_t));
	off_t offset;
	struct stat stbuf;
	char buffer[1024];
	char bufferToWrite[4200];
	int i;

label_testing:
//	test_BootSector();
	//test_PPDSend();
//	test_PPDRecv();
//	test_Socket();
	//test_Semaphore();
//	test_DumpCache();
//	test_Cache(0);
//	test_Cache_aFile();
//	test_Cache_aFile_2Clusters();

//	test_Serialize();

//	operations.create("/ggg.txt", 0, fi); //creacion en root
//	operations.create("/ggg.txt", 0, fi); //falla por existir
//	operations.create("dirInex/hhh", 0, fi); //falla por no existir directorio
//	operations.create("Dir_ghi/hhh", 0, fi); //creacion en subdirectorio

	//size(ABC.txt) = 4
//	operations.truncate("/ABC.txt", 3);
//	operations.truncate("/ABC.txt", 4);
//	operations.truncate("/ABC.txt", 5);
//	operations.truncate("/ABC.txt", 4 + 4096); //Agrego un cluster
	//size(CL2.txt) = 5120
//	operations.truncate("/CL2.txt", 4000); //Saco un cluster

//	operations.rename("/ABC.txt", "/CBA.txt");
//	operations.rename("/Dir_ghi/SubDir_ghi", "/Dir_ghi/SubDir_gghhii");

	//Cache
//	operations.open("/abc.txt", fi);
//	operations.read("/abc.txt", buffer, 1024, 0, fi);
	operations.write("/file1.txt", "HELLOABC", strlen("HELLOABC") + 1, 0, fi);
	operations.read("/abc.txt", buffer, 1024, 0, fi);
	operations.release("/abc.txt", fi);

//	operations.read("/FILE_2CL.txt", buffer, 1024, 0, fi); //Archivo de 2 clusters
//	operations.read("/ABC.txt", buffer, 1024, 0, fi);

//	operations.getattr("/Dir_ghi/SubDir_ghi", &stbuf);

/*	operations.mkdir("/Dir_ghi/SubDir_ghi", 0); //Directorio existente
	operations.mkdir("/Dir_ghi/DirNuevo", 0);   //Se debe poder crear el directorio
	operations.mkdir("/Dir_mno", 0);   //Se debe poder crear el directorio
*/

//	operations.rmdir("/def.txt"); //Result = No es un directorio
//	operations.rmdir("/Dir_ghi"); //Result = Directorio no esta vacio
//	operations.rmdir("/Dir_ghi/SubDir_ghi"); //Result = Eliminar directorio


//	operations.unlink("/def.txt");

/*
	for (i = 0; i < 4200 - 1; i++)
		bufferToWrite[i] = 'D';
	bufferToWrite[i] = '\0';
	operations.write("/def.txt", bufferToWrite, 4200, 0, fi);
*/
//	operations.readdir("/", buf, filler, offset, fi);
//	operations.readdir("/Dir_ghi", buf, filler, offset, fi);
//	operations.readdir("/Dir_ghi/SubDir_ghi", buf, filler, offset, fi);
//	operations.readdir("/directoryA/directoryB", buf, filler, offset, fi);

/*
 	operations.open("/def.txt", fi);
	operations.open("/", fi);
	operations.open("/home", fi);
	operations.open("/home/etc/arch.txt", fi);
	operations.open("/archivo.txt", fi);
	operations.open("/archivo00.txt", fi);
	operations.open("/archivoConUnNombreMuyyyyyyyyyyLargo.txt", fi);
*/
	test_ClustersLibres();


//	FreeFileAllocationTable();
#endif

//	printf("Espera llamar a fuse_main\n");
//	getchar();
	return fuse_main(argc, argv, &operations, NULL);
}
